package com.zhiqian.community.controller;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import com.zhiqian.community.entity.User;
import com.zhiqian.community.util.CommunityUtil;
import com.zhiqian.community.util.HostHolder;
import com.zhiqian.community.util.RedisKeyUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Controller
@RequestMapping("/file")
public class FileController {

    private static final Logger logger = LoggerFactory.getLogger(FileController.class);
    @Autowired
    private OSS ossClient;
    @Autowired
    private HostHolder hostHolder;
    @Value("${file-bucket}")
    private String fileBucket;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 列举文件
     * @param model
     * @return
     */
    @GetMapping("/myFile")
    public String getMyFile(Model model) {
        User user = hostHolder.getUser();
        String prefix = getFilePrefix(user.getId());
        ObjectListing objectListing = ossClient.listObjects(new ListObjectsRequest(fileBucket).withPrefix(prefix));
        List<OSSObjectSummary> summaryList = objectListing.getObjectSummaries();
        List<Map<String, Object>> result = new ArrayList<>();
        summaryList.forEach(fileObject -> {
            HashMap<String, Object> map = new HashMap<>(4);
            String key = fileObject.getKey();
            String countKey = RedisKeyUtil.getFileDownloadCountKey(key);
            Object count = redisTemplate.opsForValue().get(countKey);
            map.put("downloadCount", count == null ? 0 : (int) count);
            map.put("filename", key.substring(key.indexOf("/") + 1));
            Long size = fileObject.getSize();
            String filesize;
            if (size.compareTo(1048576L) > 0) {
                filesize = String.format("%.2f", size.doubleValue() / Double.valueOf(1048576L)) + "MB";
            } else {
                filesize = String.format("%.2f", size.doubleValue() / Double.valueOf(1024L)) + "KB";
            }
            map.put("size", filesize);
            map.put("createTime", fileObject.getLastModified());
            result.add(map);
        });
        model.addAttribute("fileInfo", result);
        return "site/my-file";
    }

    /**
     * 上传文件
     * @param file
     * @param fileName
     * @return
     */
    @PostMapping("/uploadFile")
    @ResponseBody
    public String uploadFile(MultipartFile file, String fileName) {
        if (file.getSize() == 0) {
            return CommunityUtil.getJSONString(1, "上传失败，请选择你需要上传的文件");
        }
        String name = file.getOriginalFilename();
        int index = name.lastIndexOf(".");
        if (index == -1) {
            return CommunityUtil.getJSONString(1, "上传失败，文件格式不正确");
        }
        String suffix = name.substring(index);
        if (StringUtils.isBlank(suffix)) {
            return CommunityUtil.getJSONString(1, "上传失败，文件格式不正确");
        }
        String prefix = getFilePrefix(hostHolder.getUser().getId());
        if (StringUtils.isBlank(fileName)) {
            fileName = prefix + name;
        } else {
            fileName = prefix + fileName + suffix;
        }
        try {
            ossClient.putObject(fileBucket, fileName, new ByteArrayInputStream(file.getBytes()));
            logger.info(String.format("文件上传成功 [%s] ",fileName));
        } catch (IOException e) {
            logger.error("文件上传失败：" + e.getMessage());
        }
        return CommunityUtil.getJSONString(0, "文件上传成功!");
    }

    /**
     * 下载文件
     * @param fileName
     * @param response
     * @param request
     * @throws IOException
     */
    @GetMapping("/download/{fileName}")
    public void download(@PathVariable("fileName") String fileName, HttpServletResponse response, HttpServletRequest request) throws IOException {
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String filePath = getFilePrefix(user.getId()) + fileName;
        boolean exist = ossClient.doesObjectExist(fileBucket, filePath);
        if (exist) {
            response.setContentType("application/octet-stream");
            String key = RedisKeyUtil.getFileDownloadCountKey(filePath);
            redisTemplate.opsForValue().increment(key);
            try (BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
                 OSSObject object = ossClient.getObject(fileBucket, filePath);
                 InputStream inputStream = new BufferedInputStream(object.getObjectContent())) {
                long contentLength = object.getObjectMetadata().getContentLength();
                response.setHeader("Content-Length", String.valueOf(contentLength));
                byte[] bytes = new byte[1024];
                int len;
                while ((len = inputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, len);
                }
                logger.info(String.format("文件下载成功 [%s]",filePath));
            } catch (OSSException | IOException e) {
                logger.error(String.format("文件下载失败：[%s] ", filePath) + e.getMessage());
            }
        } else {
            response.sendRedirect(request.getContextPath() + "/denied");
        }
    }

    /**
     * 删除文件
     * @param filename
     * @return
     */
    @PostMapping("/delete/{fileName}")
    @ResponseBody
    public String delete(@PathVariable("fileName")String filename){
        if (StringUtils.isBlank(filename)){
            return CommunityUtil.getJSONString(1,"删除失败，请选择你要删除的文件");
        }
        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String filePath=getFilePrefix(user.getId())+filename;
        boolean exist = ossClient.doesObjectExist(fileBucket, filePath);
        if (exist) {
            String countKey = RedisKeyUtil.getFileDownloadCountKey(filePath);
            redisTemplate.delete(countKey);
            try {
                ossClient.deleteObject(fileBucket, filePath);
                logger.info(String.format("文件删除成功 [%s]", filePath));
                return CommunityUtil.getJSONString(0, "删除成功");
            } catch (OSSException e) {
                logger.error("删除文件失败：" + e.getMessage());
            }
        }else {
            return CommunityUtil.getJSONString(0,"你要删除的文件不存在");
        }
        return CommunityUtil.getJSONString(1,"删除失败，请稍后再试");
    }

    private String getFilePrefix(Integer userId) {
        return "userId=" + userId + "/";
    }
}
